Use case: Clock calibration (polling) ¶
The configuration and status monitoring of the FDCAN clock calibration have been significantly improved from HAL1 to HAL2:
Clock calibration configuration
In HAL1, the FDCAN clock calibration is configured using the single function
HAL_FDCAN_ConfigClockCalibration(), which accepts a configuration structure containing parameters such as time quanta per bit time, calibration field length, minimum oscillator clock periods, watchdog start value, clock calibration enable flag, and clock divider. When the clock calibration is disabled by settingClockCalibration = FDCAN_CLOCK_CALIBRATION_DISABLE, the clock divider specified in the configuration is used to adjust the FDCAN clock prescaler.In HAL2, the clock calibration configuration is modularized for greater flexibility and control. The function
HAL_FDCAN_CCU_SetClockRouting()selects the clock routing path, allowing the user to enable the clock calibration unit (e.g.,HAL_FDCAN_CCU_CLOCK_ROUTE_CALIBRATION) or bypass it (HAL_FDCAN_CCU_CLOCK_ROUTE_BYPASS). Calibration parameters are then set separately usingHAL_FDCAN_CCU_SetConfigCalibrationUnit(). When the clock calibration unit is bypassed, the FDCAN clock calibration is disabled, and the clock divider can be explicitly configured usingHAL_FDCAN_SetClockDivider()to set the clock prescaler.
Get clock calibration state
In HAL1, checking the FDCAN clock calibration status requires multiple steps: waiting for the calibration state change flag using
__HAL_FDCAN_GET_FLAG(), then retrieving the calibration state withHAL_FDCAN_GetClockCalibrationState(), followed by manually clearing the calibration state change flag using__HAL_FDCAN_CLEAR_FLAG().In HAL2, this process is streamlined into a single function,
HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(), which reads the current calibration level and clears the calibration state change flag internally.
In both HAL1 and HAL2, when calibration is complete or the user wants to exit restricted mode, the user must call the appropriate function: in HAL1,
HAL_FDCAN_ExitRestrictedOperationMode(), and in HAL2,
HAL_FDCAN_DisableRestrictedOperationMode().
Sequence diagram ¶
Code snippet ¶
|
HAL1 |
HAL2 |
|---|---|
hfdcan.Instance = FDCAN1;
hfdcan.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
hfdcan.Init.Mode = FDCAN_MODE_NORMAL;
hfdcan.Init.AutoRetransmission = DISABLE;
hfdcan.Init.TransmitPause = DISABLE;
hfdcan.Init.ProtocolException = ENABLE;
hfdcan.Init.NominalPrescaler = 0x1;
hfdcan.Init.NominalTimeSeg1 = 0xF;
hfdcan.Init.NominalTimeSeg2 = 0x4;
hfdcan.Init.NominalSyncJumpWidth = 0x4;
hfdcan.Init.MessageRAMOffset = 0;
hfdcan.Init.StdFiltersNbr = 2;
hfdcan.Init.ExtFiltersNbr = 0;
hfdcan.Init.RxFifo0ElmtsNbr = 0;
hfdcan.Init.RxFifo1ElmtsNbr = 0;
hfdcan.Init.RxBuffersNbr = 2;
hfdcan.Init.RxBufferSize = FDCAN_DATA_BYTES_8;
hfdcan.Init.TxEventsNbr = 0;
hfdcan.Init.TxBuffersNbr = 1;
hfdcan.Init.TxFifoQueueElmtsNbr = 0;
hfdcan.Init.TxElmtSize = FDCAN_DATA_BYTES_8;
HAL_FDCAN_Init(&hfdcan);
sCcuConfig.ClockCalibration = ENABLE;
sCcuConfig.MinOscClkPeriods = 4;
sCcuConfig.CalFieldLength = FDCAN_CALIB_FIELD_LENGTH_64;
sCcuConfig.TimeQuantaPerBitTime = 20;
HAL_FDCAN_ConfigClockCalibration(&hfdcan, &sCcuConfig);
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_DUAL;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXBUFFER;
sFilterConfig.FilterID1 = 0x555;
sFilterConfig.RxBufferIndex = 0;
sFilterConfig.IsCalibrationMsg = 1;
HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig);
HAL_FDCAN_Start(&hfdcan);
while(HAL_FDCAN_IsRxBufferMessageAvailable(&hfdcan, FDCAN_RX_BUFFER0) == 0);
while(__HAL_FDCAN_GET_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED) == 0);
if(HAL_FDCAN_GetClockCalibrationState(&hfdcan) != FDCAN_CLOCK_BASIC_CALIBRATED)
{
Error_Handler();
}
__HAL_FDCAN_CLEAR_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED);
while(__HAL_FDCAN_GET_FLAG(&hfdcan, FDCAN_FLAG_CALIB_STATE_CHANGED) == 0);
if(HAL_FDCAN_GetClockCalibrationState(&hfdcan) != FDCAN_CLOCK_PRECISION_CALIBRATED)
{
Error_Handler();
}
HAL_FDCAN_ExitRestrictedOperationMode(&hfdcan);
|
HAL_FDCAN_Init(&hfdcan, HAL_FDCAN1);
fdcan_config.frame_format = HAL_FDCAN_FRAME_FORMAT_CLASSIC_CAN;
fdcan_config.mode = HAL_FDCAN_MODE_NORMAL;
fdcan_config.auto_retransmission = HAL_FDCAN_AUTO_RETRANSMISSION_ENABLE;
fdcan_config.transmit_pause = HAL_FDCAN_TRANSMIT_PAUSE_DISABLE;
fdcan_config.protocol_exception = HAL_FDCAN_PROTOCOL_EXCEPTION_ENABLE;
hal_fdcan_nominal_bit_timing_t fdcan_nominal_bit_timing;
fdcan_nominal_bit_timing.nominal_prescaler = 0x1;
fdcan_nominal_bit_timing.nominal_time_seg1 = 0xF;
fdcan_nominal_bit_timing.nominal_time_seg2 = 0x4;
fdcan_nominal_bit_timing.nominal_jump_width = 0x4;
fdcan_config.nominal_bit_timing = fdcan_nominal_bit_timing;
fdcan_config.message_ram_offset = 0;
fdcan_config.std_filters_nbr = 2;
fdcan_config.ext_filters_nbr = 0;
fdcan_config.rx_fifo0_elt_nbr = 0;
fdcan_config.rx_fifo1_elt_nbr = 0;
fdcan_config.rx_buffer_nbr = 2;
fdcan_config.rx_buffer_size = HAL_FDCAN_DATA_BYTES_8;
fdcan_config.tx_event_nbr = 0;
fdcan_config.tx_buffer_nbr = 1;
fdcan_config.tx_fifo_queue_elt_nbr = 0;
fdcan_config.tx_fifo_queue_elt_size = HAL_FDCAN_DATA_BYTES_8;
HAL_FDCAN_SetConfig(&hfdcan, &fdcan_config);
hal_fdcan_ccu_clock_route_t clock_route = HAL_FDCAN_CCU_CLOCK_ROUTE_CALIBRATION;
HAL_FDCAN_CCU_SetClockRouting(&hfdcan, clock_route);
ccu_config.min_osc_clk_periods = 4;
ccu_config.calibration_field_length = HAL_FDCAN_CCU_FIELD_LENGTH_64;
ccu_config.time_quanta_per_bit_time = 20;
HAL_FDCAN_CCU_SetConfigCalibrationUnit(&hfdcan, &ccu_config);
filter_calibration.id_type = HAL_FDCAN_ID_STANDARD,
filter_calibration.filter_index = 0U,
filter_calibration.filter_type = HAL_FDCAN_FILTER_TYPE_DUAL,
filter_calibration.filter_config = HAL_FDCAN_FILTER_TO_RX_BUFFER,
filter_calibration.filter_id1 = 0x555,
filter_calibration.rx_buffer_index = HAL_FDCAN_RX_BUFFER0,
filter_calibration.message_type = HAL_FDCAN_MESSAGE_CALIBRATION,
HAL_FDCAN_SetFilter(&hfdcan, &filter_calibration);
HAL_FDCAN_Start(&hfdcan);
while(HAL_FDCAN_GetRxBufferMessageStatusAndClearFlag(&hfdcan, HAL_FDCAN_RX_BUFFER0) == 0);
if(HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(&hfdcan, 10) != HAL_FDCAN_CCU_CAL_LEVEL_BASIC_CALIBRATED)
{
Error_Handler();
}
if(HAL_FDCAN_CCU_GetCalibrationLevelAndClearFlag(&hfdcan, 10) != HAL_FDCAN_CCU_CAL_LEVEL_PRECISION_CALIBRATED)
{
Error_Handler();
}
HAL_FDCAN_DisableRestrictedOperationMode(&hfdcan);
|
Key changes ¶
HAL2 modularizes clock calibration configuration with dedicated CCU routing and configuration APIs.
HAL2 streamlines calibration status monitoring with a single API that also clears the status flag.
![@startuml
participant App as "User Application"
participant "<font color=black><b>HAL FDCAN" as FDCAN #19DD3A
participant "FDCAN Hardware System" as FDCAN_HW
== FDCAN Initialization and Configuration ==
App -> FDCAN: HAL_FDCAN_Init(&hfdcan)
FDCAN --> App: <color green>HAL_OK
App -> FDCAN: HAL_FDCAN_ConfigClockCalibration(&hfdcan, &sCcuConfig)
FDCAN --> App: <color green>HAL_OK
== Configure Reception Filters ==
App -> FDCAN: HAL_FDCAN_ConfigFilter(&hfdcan, &sFilterConfig) [Calibration Filter]
note right
Configure at least one filter
with IsCalibrationMsg = 1
endnote
FDCAN --> App: <color green>HAL_OK
App-> FDCAN: HAL_FDCAN_Start()
App <-- FDCAN:<color green>HAL_OK
App -> FDCAN: HAL_FDCAN_IsRxBufferMessageAvailable(&hfdcan, FDCAN_RX_BUFFER0)
FDCAN_HW <- : FDCAN **First** Rx buffer new message
note right
First calibration Message available
endnote
FDCAN --> App: 1 (available)
loop Wait for calibration state change flag
App -> FDCAN: __HAL_FDCAN_GET_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
FDCAN --> App: 1 (flag set)
end
App -> FDCAN: HAL_FDCAN_GetClockCalibrationState(&hfdcan)
FDCAN_HW --> FDCAN_HW: Basic calibration done and state changed
hnote over FDCAN_HW
<font color=orange><b>BASIC_CALIBRATED</b>
end note
FDCAN --> App: <font color=orange>BASIC_CALIBRATED
App -> FDCAN: __HAL_FDCAN_CLEAR_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
FDCAN --> App: <color green>HAL_OK
FDCAN_HW <- : FDCAN **second** Rx buffer new message
note right
Second calibration Message available
endnote
FDCAN --> App: 1 (available)
loop Wait for calibration state change flag
App -> FDCAN: __HAL_FDCAN_GET_FLAG(FDCAN_FLAG_CALIB_STATE_CHANGED)
FDCAN --> App: 1 (flag set)
end
App -> FDCAN: HAL_FDCAN_GetClockCalibrationState(&hfdcan)
FDCAN_HW --> FDCAN_HW: Precision calibration done and state changed
hnote over FDCAN_HW
<font color=green><b>PRECISION_CALIBRATED</b>
end note
FDCAN --> App: <font color=green> PRECISION_CALIBRATED
== Exit Restricted Operation Mode ==
App -> FDCAN: HAL_FDCAN_ExitRestrictedOperationMode(&hfdcan)
hNote over FDCAN_HW: The FDCAN in <color green><b>Normal Operation Mode</b></color>
FDCAN --> App: <color green>HAL_OK
Note over App: The FDCAN is calibrated and ready to be used for communication.
@enduml](../../../../../_images/plantuml-22cb08751cb96fa1454af2fa77b12d9f9010d2be.png)
